#pragma once
#include <iostream>
#include <pthread.h>
#include <semaphore.h>
#include <assert.h>
// #define QUEUENUM 10//不想将其暴露可以放在类里面

template <class T>
class RingQueue
{
private:
    const static int QUEUENUM = 10;

public:
    RingQueue(const size_t p_pos = 0, size_t c_pos = 0)
        : _p_pos(p_pos), _c_pos(c_pos)
    {
        pthread_mutex_init(&_queue_mutex, nullptr);
        pthread_mutex_init(&_producers_mutex, nullptr);
        pthread_mutex_init(&_consumer_mutex, nullptr);
        int ret = sem_init(&_empty_sem, 0, QUEUENUM);
        assert(ret == 0);
        ret = sem_init(&_data_sem, 0, 0);
        assert(ret == 0);
    }
    ~RingQueue()
    {
        pthread_mutex_destroy(&_queue_mutex);
        pthread_mutex_destroy(&_producers_mutex);
        pthread_mutex_destroy(&_consumer_mutex);
        sem_destroy(&_empty_sem);
        sem_destroy(&_data_sem);
    }

    void push(const T &in)
    {

        int ret = sem_wait(&_empty_sem);
        assert(ret == 0);
        arr[_p_pos++] = in;
        _p_pos = (_p_pos + QUEUENUM) % QUEUENUM;
        ret = sem_post(&_data_sem);
        assert(ret == 0);
    }
    void pop(T *const out)
    {
        int ret = sem_wait(&_data_sem);
        assert(ret == 0);
        *out = arr[_c_pos++];
        _c_pos = (_c_pos + QUEUENUM) % QUEUENUM;
        ret = sem_post(&_empty_sem);
        assert(ret == 0);
    }

private:
    T arr[QUEUENUM];

    sem_t _empty_sem; // 空盘子对应的信号量

    sem_t _data_sem; // 数据个数对应的信号量

    size_t _p_pos;
    size_t _c_pos;

    pthread_mutex_t _queue_mutex; // 环形队列满和空对应的互斥锁

    // 如果加入多生产和多消费，需要加上
    pthread_mutex_t _producers_mutex; // 生产者之间的互斥锁
    pthread_mutex_t _consumer_mutex;  // 消费者之间的互斥锁
};
